import * as vscode from "vscode"
import * as URI from "uri-js";
import { VerticalDiffHandler} from "./handler";
import { ChatMessage } from '../../../core';
import {streamDiffLines} from "../../../core/edit/streamDiffLines"

export interface VerticalDiffCodeLens {
    start: number;
    numRed: number;
    numGreen: number;
}

export class VerticalDiffManager {

    private userChangeListener: vscode.Disposable | undefined;

    public refreshCodeLens: () => void = () => {};

    fileUriToCodeLens: Map<string, VerticalDiffCodeLens[]> = new Map();
    
    private fileUriToHandler: Map<string, VerticalDiffHandler> = new Map();

    getHandlerForFile(fileUri: string) {
        return this.fileUriToHandler.get(fileUri);
    }

    createVerticalDiffHandler(
        fileUri: string,
        startLine: number,
        endLine: number
      ) {
        if (this.fileUriToHandler.has(fileUri)) {
            this.fileUriToHandler.get(fileUri)?.clear(false);
            this.fileUriToHandler.delete(fileUri);
        }
        
        const editor = vscode.window.activeTextEditor; // TODO
        if (editor && URI.equal(editor.document.uri.toString(), fileUri)) {
            const handler = new VerticalDiffHandler(
                startLine,
                endLine,
                editor,
                this.fileUriToCodeLens,
                ()=>{},
                this.refreshCodeLens,
                {input:"", onStatusUpdate: () => {}},
            );
            this.fileUriToHandler.set(fileUri, handler);
            return handler;
        } else {
            return undefined;
        }
    }

    async streamEdit(streamChat:AsyncGenerator<ChatMessage>, range?: vscode.Range) {
        let editor = vscode.window.activeTextEditor;

        if (!editor) {
          return undefined;
        }
    
        const fileUri = editor.document.uri.toString();
    
        let startLine, endLine: number;
    
        if (range) {
          startLine = range.start.line;
          endLine = range.end.line;
        } else {
          startLine = editor.selection.start.line;
          endLine = editor.selection.end.line;
        }
    
        await new Promise((resolve) => {
            setTimeout(resolve, 200);
        });

        // Create new handler with determined start/end
        const diffHandler = this.createVerticalDiffHandler(
            fileUri,
            startLine,
            endLine
        );
        if (!diffHandler) {
            console.warn("Issue occured while creating new vertical diff handler");
            return undefined;
        }
      
        let selectedRange = diffHandler.range;
    
        // Only if the selection is empty, use exact prefix/suffix instead of by line
        if (selectedRange.isEmpty) {
            selectedRange = new vscode.Range(
                editor.selection.start.with(undefined, 0),
                editor.selection.end.with(undefined, Number.MAX_SAFE_INTEGER),
            );
        }
        // 获取选中的文本
        const rangeContent = editor.document.getText(selectedRange);

        const streamedLines: string[] = [];
        async function* recordedStream() {
            const stream = streamDiffLines(rangeContent,streamChat);
            for await (const line of stream) {
              if (line.type === "new" || line.type === "same") {
                streamedLines.push(line.line);
              }
              yield line;
            }
        }
        
        await diffHandler.run(recordedStream());
    }


    async acceptRejectVerticalDiffBlock(
        accept: boolean,
        fileUri?: string,
        index?: number,
      ) {
        if (!fileUri) {
          const activeEditor = vscode.window.activeTextEditor;
          if (!activeEditor) {
            return;
          }
          fileUri = activeEditor.document.uri.toString();
        }
    
        if (typeof index === "undefined") {
          index = 0;
        }
    
        const blocks = this.fileUriToCodeLens.get(fileUri);
        const block = blocks?.[index];
        if (!blocks || !block) {
          return;
        }
    
        const handler = this.getHandlerForFile(fileUri);
        if (!handler) {
          return;
        }
    
        // Disable listening to file changes while continue makes changes
        this.disableDocumentChangeListener();
    
        // CodeLens object removed from editorToVerticalDiffCodeLens here
        await handler.acceptRejectBlock(
          accept,
          block.start,
          block.numGreen,
          block.numRed,
        );
    
        if (blocks.length === 1) {
          this.clearForfileUri(fileUri, true);
        } else {
          // Re-enable listener for user changes to file
          this.enableDocumentChangeListener();
        }
    
        this.refreshCodeLens();
    }

    clearForfileUri(fileUri: string | undefined, accept: boolean) {
        if (!fileUri) {
          const activeEditor = vscode.window.activeTextEditor;
          if (!activeEditor) {
            return;
          }
          fileUri = activeEditor.document.uri.toString();
        }
    
        const handler = this.fileUriToHandler.get(fileUri);
        if (handler) {
          handler.clear(accept);
          this.fileUriToHandler.delete(fileUri);
        }
    
        this.disableDocumentChangeListener();
    
        vscode.commands.executeCommand("setContext", "continue.diffVisible", false);
    }

    // Creates a listener for document changes by user.
    private enableDocumentChangeListener(): vscode.Disposable | undefined {
        if (this.userChangeListener) {
        //Only create one listener per file
        return;
        }

        this.userChangeListener = vscode.workspace.onDidChangeTextDocument(
        (event) => {
            // Check if there is an active handler for the affected file
            const fileUri = event.document.uri.toString();
            const handler = this.getHandlerForFile(fileUri);
            if (handler) {
            // If there is an active diff for that file, handle the document change
            this.handleDocumentChange(event, handler);
            }
        },
        );
    }

    private handleDocumentChange(
        event: vscode.TextDocumentChangeEvent,
        handler: VerticalDiffHandler,
      ) {
        // Loop through each change in the event
        event.contentChanges.forEach((change) => {
          // Calculate the number of lines added or removed
          const linesAdded = change.text.split("\n").length - 1;
          const linesDeleted = change.range.end.line - change.range.start.line;
          const lineDelta = linesAdded - linesDeleted;
    
          // Update the diff handler with the new line delta
          handler.updateLineDelta(
            event.document.uri.toString(),
            change.range.start.line,
            lineDelta,
          );
        });
      }

    // Listener for user doc changes is disabled during updates to the text document by continue
    public disableDocumentChangeListener() {
        if (this.userChangeListener) {
        this.userChangeListener.dispose();
        this.userChangeListener = undefined;
        }
    }

}